#include "Texture.h"
#include "math.h"

/* Taken from Huge Elias's tutorial on Perlin noise 
 * freespace.virgin.net/hugo.elias/models/m_perlin.htm 
 */


float noise1(int seed) {
    seed = (seed << 13) ^ seed;
    return (1.0 - ((seed * (seed * seed * 15731 + 789221) + 1376312589) & 
                0x7fffffff) / 1073741824.0);
}

float noise2(int seed) {
    seed = (seed << 13) ^ seed;
    return (1.0 - ((seed * (seed * seed * 16333 + 1300133) + 179425177) & 
                0x7fffffff) / 1073741824.0);
}
float noise3(int seed) {
    seed = (seed << 13) ^ seed;
    return (1.0 - ((seed * (seed * seed * 105527 + 8831) + 179426323) & 
                0x7fffffff) / 1073741824.0);
}
float noise4(int seed) {
    seed = (seed << 13) ^ seed;
    return (1.0 - ((seed * (seed * seed * 118211 + 459763) + 2111810633) & 
                0x7fffffff) / 1073741824.0);
}

float noise_2D(int x, int y) {
    int n = x + y * 57;
    n = (n << 13) ^ n;
    return (1.0 - ((n * (n * n * 15731 + 1300133) + 2111810633) & 0x7fffffff)) /
        1073741824.0;
}

float smoothNoise_2D(int x, int y) {
    float corners = (noise_2D(x - 1, y - 1) + noise_2D(x + 1, y - 1) +
                     noise_2D(x - 1, y + 1) + noise_2D(x + 1, y + 1)) / 16.f;
    float sides =   (noise_2D(x - 1, y) + noise_2D(x + 1, y) +
                     noise_2D(x, y - 1) + noise_2D(x, y+1)) /  8.f;
    float center =  (noise_2D(x, y)) / 4.f;

    return corners + sides + center;
}

/* cosine interpolation */
float cosint(float a, float b, float x) {
    float theta = x * PI;
    float inter = (1.f - cos(theta)) * 0.5;

    return a * (1.f - inter) + b * inter;
}

float interpolate_2D(float x, float y) {
    int int_x = (int) x;
    int int_y = (int) y;
    float frac_x = x - int_x;
    float frac_y = y - int_y;
    float v1, v2, v3, v4, i1, i2, ret;

    v1 = smoothNoise_2D(int_x, int_y);
    v2 = smoothNoise_2D(int_x + 1, int_y);
    v3 = smoothNoise_2D(int_x, int_y + 1);
    v4 = smoothNoise_2D(int_x + 1, int_y + 1);

    i1 = cosint(v1, v2, frac_x);
    i2 = cosint(v3, v4, frac_x);

    return cosint(i1, i2, frac_y);
    
}

float perlinNoise(float x, float y, float persist, int octaves) {
    float total = 0;
    int i;
    float amplitude, frequency;


    for (i = 0; i < octaves; i++) {
        frequency = 2 << i;
        amplitude = pow(persist, i);

        total += interpolate_2D(x * frequency, y * frequency) * amplitude;
    }

    return total;
}
